<?php
namespace waf\client\mysql;

use \waf\Waf;
use \waf\log\WLog;

/**
 * 操作mysql的类
 *
 * Class WMysqlClient
 * @package waf\client\mysql
 */
class WMysqlClient
{
    // mysql obj
    private $_objMysql  = null;
    private $_conf      = array();
    private $_sql       = '';
    private $_arrParams = array();

    // sql 执行类型
    private $_sql_type_insert   = 1;
    private $_sql_type_delete   = 2;
    private $_sql_type_update   = 3;
    private $_sql_type_find     = 4;
    private $_sql_type_find_all = 5;

    function __construct($conf = array())
    {
        WLog::logInfo("in. conf=".WLog::toJson($conf), __FILE__, __LINE__, __FUNCTION__);

        if($conf)
        {
            $swoole_mysql = new \Swoole\Coroutine\MySQL();
            $ret = $swoole_mysql->connect([
                'host'          => $conf['host'] ?? '127.0.0.1',
                'user'          => $conf['user'] ?? 'root',
                'password'      => $conf['password'] ?? 'root',
                'database'      => $conf['database'] ?? 'test',
                'port'          => $conf['port'] ?? 3306,
                'timeout'       => $conf['timeout'] ?? 5,
                'charset'       => $conf['charset'] ?? 'utf8',
                'strict_type'   => $conf['strict_type'] ?? false,
                'fetch_mode'    => $conf['fetch_mode'] ?? true,
            ]);
            if($ret)
            {
                $this->_objMysql = $swoole_mysql;
            }else{
                WLog::logError("mysql connect error. connect_error = {$swoole_mysql->connect_error}, connect_errno =  {$swoole_mysql->connect_errno}", __FILE__, __LINE__, __FUNCTION__);
            }

            $this->_conf    = $conf;
        }

        WLog::logInfo("out.", __FILE__, __LINE__, __FUNCTION__);
    }

    //================== 快捷操作系列
    /**
     * insert 操作的快捷操作
     *
     * 示例：
     *      $data   = array(
                'name'  => time(),
                'f1'    => 1,
            );
     *      insert('mytest2', $data)->execute()
     *
     * @param string $table_name    // 表名
     * @param array $data           // 需要插入的数据 array('k1' => v1, 'k2' => v2)
     * @return $this
     */
    public function insert($table_name = '', $data = array())
    {
        if($table_name && $data && is_array($data))
        {
            $strSql     = "";
            $arrKeys    = array();
            $arrValues  = array();
            $arrParams  = array();
            $i = 0;
            foreach($data as $k => $v)
            {
                $arrKeys[$i]    = '`'.$k.'`';
                $arrValues[$i]  = '?';
                $arrParams[$i]  = $v;
                $i++;
            }

            if($arrKeys && $arrValues)
            {
                $strSql = 'insert into '.$this->_conf['prefix'].$table_name.' ('.implode(', ', $arrKeys).') values ('.implode(', ', $arrValues).')';

                $this->_sql = $strSql;
                $this->_arrParams   = $arrParams;
            }
        }

        return $this;
    }

    /**
     * update 操作的快捷操作
     *
     * 示例：
     *      $arrUpdateData  = array(
                    'f1'    => 100,
                    'f2'    => 200,
                );
            $where          = "where id = 9";
            $arrWhereParams = array();
     *      update('mytest2', $arrUpdateData, $where, $arrWhereParams)->execute()
     *
     * @param string $table_name        // 表名
     * @param array $data               // 需要更新的数据 array('k1' => v1, 'k2' => v2)
     * @param string $where             // where条件，必须传值
     * @param array $arrWhereParams     // where条件的预处理数据
     * @return $this
     */
    public function update($table_name = '', $data = array(), $where = '', $arrWhereParams = array())
    {
        WLog::logInfo("in. table_name = $table_name, data=".WLog::toJson($data).", where = $where, arrWhereParams = ".WLog::toJson($arrWhereParams), __FILE__, __LINE__, __FUNCTION__);

        if($table_name && $data && is_array($data) && $where)
        {
            $strSql     = "";
            $arrKeys    = array();
            $arrParams  = array();
            $i = 0;

            foreach($data as $k => $v)
            {
                $arrKeys[$i]    = '`'.$k.'` = ?';
                $arrParams[$i]  = $v;
                $i++;
            }

            if($arrWhereParams && is_array($arrWhereParams))
            {
                foreach($arrWhereParams as $v)
                {
                    $arrParams[] = $v;
                }
                unset($arrWhereParams);
            }

            if($arrKeys)
            {
                $strSql = 'update '.$this->_conf['prefix'].$table_name.' set '.implode(', ', $arrKeys).' '.$where;
                $this->_sql         = $strSql;
                $this->_arrParams   = $arrParams;
            }
        }

        return $this;
    }

    /**
     * delete 操作的快捷操作
     *
     * 示例 ：
     *  delete('mytest2', "where id = ?", [7])->execute()
     *
     * @param string $table_name        // 表名
     * @param string $where             // where条件，必须传值
     * @param array $arrWhereParams     // where条件的预处理数据
     * @return $this
     */
    public function delete($table_name = '', $where = '', $arrWhereParams = array())
    {
        WLog::logInfo("in. table_name = $table_name, where = $where, arrWhereParams = ".WLog::toJson($arrWhereParams), __FILE__, __LINE__, __FUNCTION__);

        if($table_name && $where)
        {
            $this->_sql         = "delete from ".$this->_conf['prefix'].$table_name.' '.$where;
            $this->_arrParams   = $arrWhereParams;
        }
        return $this;
    }

    /**
     * select 操作的快捷操作
     *
     * 示例：
     *  select('mytest2', '*', "where id = ?", [8])->findAll()
     *
     * @param string $table_name        // 表名
     * @param string $select_field      // 查询的字段名: * or id,name
     * @param string $where             // where条件，必须传值
     * @param array $arrWhereParams     // where条件的预处理数据
     * @return $this
     */
    public function select($table_name = '', $select_field = '*', $where = '', $arrWhereParams = array())
    {
        WLog::logInfo("in. table_name = $table_name, select_field = $select_field, where = $where, arrWhereParams = ".WLog::toJson($arrWhereParams), __FILE__, __LINE__, __FUNCTION__);

        if($table_name && $select_field && $where)
        {
            $this->_sql         = "select {$select_field} from ".$this->_conf['prefix'].$table_name.' '.$where;
            $this->_arrParams   = $arrWhereParams;
        }
        return $this;
    }

    /**
     * 查看快捷操作生成的sql和参数
     *
     * @return array
     */
    public function getSqlAndParams()
    {
        return ['sql' => $this->_sql, 'params' => $this->_arrParams];
    }
    //================== 纯sql系列
    /**
     * 执行sql, 主要用于 insert|update|delete
     *
     *  示例:
     *  execute("insert into mytest2 values ('', 'name4', ?);", [20])
     *  execute("delete from mytest2 where id = ?", [3])
     *  execute("update mytest2 set pass = 'passaa' where id = ? and `name` = ?", [6, 'name4'])
     *
     * @param string $sql       // 待执行的sql
     * @param array $arrParams  // 使用预处理的参数
     * @param int $timeout      // 超时时间，在规定的时间内MySQL服务器未能返回数据，底层将返回false
     * @return bool or array    // 执行失败false    other:true or array
     */
    public function execute($sql = '', $arrParams = array(),$timeout = 10)
    {
        return $this->doExecute($sql, $arrParams, $timeout);
    }

    /**
     * 执行sql, 主要用于 select
     *
     * 示例:
     *  find("select * from mytest2")
     *
     * @param string $sql       // 待执行的sql
     * @param array $arrParams  // 使用预处理的参数
     * @param int $timeout      // 超时时间，在规定的时间内MySQL服务器未能返回数据，底层将返回false
     * @return bool or array    // 执行失败false    other:true or array
     */
    public function find($sql = '', $arrParams = array(), $timeout = 10)
    {
        return $this->doExecute($sql, $arrParams, $timeout, $this->_sql_type_find);
    }

    /**
     * 执行sql, 主要用于 select
     *
     * 示例:
     *  findAll("select * from mytest2 where id = ?", [6])
     *
     * @param string $sql       // 待执行的sql
     * @param array $arrParams  // 使用预处理的参数
     * @param int $timeout      // 超时时间，在规定的时间内MySQL服务器未能返回数据，底层将返回false
     * @return bool or array    // 执行失败false    other:true or array
     */
    public function findAll($sql = '', $arrParams = array(), $timeout = 10)
    {
        return $this->doExecute($sql, $arrParams, $timeout, $this->_sql_type_find_all);
    }

    /**
     * 执行sql
     *
     * @param string $sql       // 待执行的sql
     * @param array $arrParams  // 使用预处理的参数
     * @param int $timeout      // 超时时间，在规定的时间内MySQL服务器未能返回数据，底层将返回false
     * @param int $type         // 执行类型，目前用于区分 find && findAll
     * @return bool or array    // 执行失败false    other:true or array
     */
    protected function doExecute($sql = '', $arrParams = array(), $timeout = 10, $type = 0)
    {
        $ret    = false;

        // 全局，便捷操作
        if(empty($sql) && $this->_sql)
        {
            $sql        = $this->_sql;
            $arrParams  = $this->_arrParams;
        }
        WLog::logInfo("in. timeout = {$timeout}, sql = $sql, arrParams = ".WLog::toJson($arrParams), __FILE__, __LINE__, __FUNCTION__);

        if($sql && $this->_objMysql && $this->_objMysql->connected)
        {
            $stmt = $this->_objMysql->prepare($sql);
            if ($stmt == false)
            {
                WLog::logError("prepare fail. sql = $sql, errno = {$this->_objMysql->errno}, error = {$this->_objMysql->error}", __FILE__, __LINE__, __FUNCTION__);
            }
            else
            {
                $ret = $stmt->execute($arrParams, $timeout);
                if($ret !== false)
                {
                    switch ($type)
                    {
                        case $this->_sql_type_find:
                            $ret = $stmt->fetch();
                            break;
                        case $this->_sql_type_find_all:
                            $ret = $stmt->fetchAll();
                            break;
                    }
                }else{
                    WLog::logError("execute fail. arrParams = ".WLog::toJson($arrParams), __FILE__, __LINE__, __FUNCTION__);
                }
            }
        }

        WLog::logDebug("out. ret = ".WLog::toJson($ret), __FILE__, __LINE__, __FUNCTION__);
        return $ret;
    }

    /**
     * 检查mysql是否连接成功
     *
     * @return bool
     */
    public function isConnect()
    {
        $isRet  = false;

        if($this->_objMysql && $this->_objMysql->connected)
        {
            $isRet = true;
        }

        return $isRet;
    }

    /**
     * 获取一条插入记录id
     *
     * @return int
     */
    public function getLastInsertId()
    {
        $intRet = 0;
        $intRet = $this->_objMysql->insert_id;
        return $intRet;
    }

    /**
     * 隐藏技能，透传调用 \Swoole\Coroutine\MySQL
     *
     * @param $func
     * @param $args
     * @return bool
     */
    public function __call($func, $args)
    {
        $ret    = false;
        WLog::logInfo("in. func = {$func}, args=".WLog::toJson($args), __FILE__, __LINE__, __FUNCTION__);
        if($this->_objMysql)
        {
            if (method_exists($this->_objMysql, $func))
            {
                $ret = $this->_objMysql->$func(...$args);
            }
        }

        return $ret;
    }

    function __destruct()
    {
        WLog::logInfo("in.", __FILE__, __LINE__, __FUNCTION__);
        if($this->_objMysql)
            unset($this->_objMysql);
        WLog::logInfo("out.", __FILE__, __LINE__, __FUNCTION__);
    }
}
?>
